#include "stdafx.h"
#include "stiViewer.h"
#include "stdio.h"

CSTIviewer::CSTIviewer()
  : m_pbFileData(NULL)
  , m_pbWholeBMPData(NULL)
  , m_pwPageData(NULL)
  , m_pHeader(NULL)
  , m_pPalette(NULL)
  , m_pFat(NULL)
  , m_pRecord(NULL)
  , m_pwPosition(NULL)
  , m_pwPage(NULL)
  , m_pbPos(NULL)
  , m_pbData(NULL)
  , m_pbPages(NULL)
  , m_nDataSize(0)
  , m_bFlags(0)
  , m_nLastPage(-1)
{
  m_pchErrNames[0] = "E_OK";
  m_pchErrNames[1] = "E_ERROR";
  m_pchErrNames[2] = "E_CANT_OPEN_FILE";
  m_pchErrNames[3] = "E_NO_PICTURE";
  m_pchErrNames[4] = "E_NO_PAGE";
  m_pchErrNames[5] = "E_NO_FILENAME";
  m_pchErrNames[6] = "E_BAD_FILE";
  m_pchErrNames[7] = "E_CANT_READ_FILE";
  m_pchErrNames[8] = "E_BAD_FORMAT";
}

CSTIviewer::~CSTIviewer()
{
  closeImage();
}

const char * CSTIviewer::getErrName( int err ) const
{
  if ( err <=0 || err > 8 )
    return "Bad error value";

  return m_pchErrNames[err];
}


int CSTIviewer::openFile( const char * chFileName )
{
  if ( !chFileName )
    return E_NO_FILENAME;

  if ( m_pbFileData )
  {
    delete [] m_pbFileData; m_pbFileData = NULL;
    delete [] m_pbWholeBMPData; m_pbWholeBMPData = NULL;
//    delete [] m_pwPageData; m_pwPageData = NULL;
  }
  
  m_bFlags = 0;
  HANDLE hFile = INVALID_HANDLE_VALUE;
  if ( ( hFile = CreateFile( chFileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) )== INVALID_HANDLE_VALUE )
    return E_CANT_OPEN_FILE;

  int nFileSize = GetFileSize( hFile, NULL );
  if ( nFileSize == (DWORD)-1 )
    return E_BAD_FILE;
 
  m_pbFileData = new BYTE[nFileSize];
  DWORD readed;
  
  if ( !ReadFile( hFile, m_pbFileData, nFileSize, &readed, NULL ) )
  {
    CloseHandle( hFile );
    return E_CANT_READ_FILE;
  }
  CloseHandle( hFile );

  if ( isPicture( m_pbFileData ) )
    return E_BAD_FORMAT;
  
  int nRet = 0;
  if ( nRet = openPicture( m_pbFileData, nFileSize, &m_tpicPictureParameters) )
    return nRet;
  
  int nPages;
  if ( nRet = getNumPages( nPages ) )
    return nRet;
  
  if ( nPages == 1 )
    return openPage( 0 );
  return E_OK;
}

int CSTIviewer::openPage( int nNumber )
{
  if (!( m_bFlags & enPictureOk ))
    return E_NO_PICTURE;
  if ( m_nLastPage != -1 && nNumber != m_nLastPage )
    delete [] m_pbWholeBMPData;
    
  int ret = openPage( nNumber, &m_tpagPageParameters );

  if ( ret )
    return ret;

  m_nLastPage = nNumber;

  m_nAlignedWidth = m_tpagPageParameters.Width + ((m_tpagPageParameters.Width%4) ? (4-m_tpagPageParameters.Width%4) : 0);
  
  int nOutDataSize =  sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + m_nAlignedWidth*m_tpagPageParameters.Height*2 + 256*sizeof(PALETTEENTRY);
  m_pbWholeBMPData = new BYTE[ nOutDataSize ];

  if ( !m_pbWholeBMPData )
    return E_ERROR;

  m_pbBMPImageData = (m_pbWholeBMPData + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256*sizeof(PALETTEENTRY) );
  ZeroMemory( m_pbBMPImageData, m_nAlignedWidth*m_tpagPageParameters.Height*2);

  m_pbBMPPaletteData = m_pbWholeBMPData + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
  ZeroMemory( m_pbBMPPaletteData, 256*sizeof(PALETTEENTRY));
  getPalette( (PALETTEENTRY*)m_pbBMPPaletteData );

  for ( int i = 0; i < m_tpagPageParameters.Height; i++ )
    getLine( m_pbBMPImageData + i*m_nAlignedWidth, m_tpagPageParameters.Width );  
  swapLines( m_pbBMPImageData, m_tpagPageParameters.Height, m_nAlignedWidth );
  
  BITMAPFILEHEADER *bmfh = (BITMAPFILEHEADER *)m_pbWholeBMPData;
  BITMAPINFOHEADER *bmif = (BITMAPINFOHEADER *)(m_pbWholeBMPData + sizeof(BITMAPFILEHEADER) );
  
  bmfh->bfType           = 19778;
  bmfh->bfSize           = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + m_nAlignedWidth*m_tpagPageParameters.Height*2 + 256*sizeof(PALETTEENTRY);
  bmfh->bfReserved1      = 0;
  bmfh->bfReserved2      = 0;
  bmfh->bfOffBits        = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(PALETTEENTRY) * 256;

  bmif->biSize	        = sizeof(BITMAPINFOHEADER);  //	specifies the size of the BITMAPINFOHEADER structure, in bytes.
  bmif->biWidth	        = m_tpagPageParameters.Width;//hImage->usWidth; //	specifies the width of the image, in pixels.
	bmif->biHeight	      = m_tpagPageParameters.Height;//hImage->usHeight; // specifies the height of the image, in pixels.
	bmif->biPlanes	      = 1;	 // specifies the number of planes of the target device, must be set to zero.
	bmif->biBitCount	    = 8;	 // specifies the number of bits per pixel.
	bmif->biCompression	  = 0;   //	Specifies the type of compression, usually set to zero (no compression).
	bmif->biSizeImage	    = 0;   //	specifies the size of the image data, in bytes. If there is no compression, it is valid to set this member to zero.
	bmif->biXPelsPerMeter	= 0;   //	specifies the the horizontal pixels per meter on the designated targer device, usually set to zero.
	bmif->biYPelsPerMeter	= 0;   //	specifies the the vertical pixels per meter on the designated targer device, usually set to zero.
	bmif->biClrUsed	      = 0;   //	specifies the number of colors used in the bitmap, if set to zero the number of colors is calculated using the biBitCount member.
	bmif->biClrImportant	= 0;


  HANDLE hFile;
  DWORD  written;
  char chFileName[30];
  sprintf( chFileName, "result.bmp",nNumber );
  if ( ( hFile = CreateFile( chFileName, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ) )!= INVALID_HANDLE_VALUE )
  {
    WriteFile( hFile, m_pbWholeBMPData, nOutDataSize, &written, 0 );
    CloseHandle( hFile );
  }

  return E_OK;
}

int CSTIviewer::openPage( int Page, struct TPagePar *Par )
{
  if( m_pHeader->Type==0x04 )
  {
    Par->Width  = m_pHeader->Width;
    Par->Height = m_pHeader->Height;
    m_pwPosition= (WORD *)(m_pHeader+1);
    m_bFlags |= enPageOk;
    return E_OK;
  }
  if( m_pHeader->Type==0x28 || m_pHeader->Type==0x29 )
  {
    if( Page < 0 || Page >= m_pHeader->Count )
      return E_ERROR;
    m_pRecord   = &m_pFat[Page];
    Par->Width  = m_pRecord->Width;
    Par->Height = m_pRecord->Height;
    Par->DispX  = m_pRecord->DispX;
    Par->DispY  = m_pRecord->DispY;
    m_pbPos     = (BYTE *)(m_pRecord->Disp+(BYTE *)(m_pFat+m_pHeader->Count));
    m_bFlags |= enPageOk;
    return E_OK;
  }
  return E_ERROR;
}

int CSTIviewer::getNumPages( int & nPages ) const
{
  if ( m_bFlags & enPictureOk )
  {
    nPages = m_tpicPictureParameters.NumPages;
    return E_OK;
  }
  return E_NO_PICTURE;
}

int CSTIviewer::getPageRes( int & nWidth, int & nHeight ) const
{
  if ( m_bFlags & enPictureOk )
  {
    nWidth = m_tpagPageParameters.Width;
    nHeight= m_tpagPageParameters.Height;
    return E_OK;
  }
  return E_NO_PAGE;
}

int CSTIviewer::getColorDepth( int & nDepth ) const
{
  if ( m_bFlags & enPictureOk )
  {
    nDepth = m_tpicPictureParameters.Depth;
    return E_OK;
  }
  return E_NO_PICTURE;
}

int CSTIviewer::isPicture( BYTE *Data ) const
{
  if(Data[0]!='S'||Data[1]!='T'||Data[2]!='C'||Data[3]!='I')
    return E_ERROR;
  return E_OK;
}

int CSTIviewer::openPicture( BYTE *Data, int DataSize, struct TPicPar *Par )
{
  m_pbData    = Data;
  m_nDataSize = DataSize;
  m_pHeader   = (StiHeader*)m_pbData;
  m_pPalette  = (TColor*)(m_pHeader+1);
  m_pFat      = (StiRecord *)(m_pPalette+256);
  if(m_pHeader->Type == 0x04)
  {
    Par->Type=0;
    Par->NumPages=DataSize==sizeof(StiHeader)?0:1;
    Par->Transparent=0;
    Par->Depth=16;
    m_bFlags |= enPictureOk;
    return E_OK;
  }
  else
  {
    Par->Type         = 1;
    Par->NumPages     = m_pHeader->Count;
    Par->Transparent  = 1;
    Par->Red          = m_pPalette[0][0];
    Par->Green        = m_pPalette[0][1];
    Par->Blue         = m_pPalette[0][2];
    Par->Depth        = 8;
    m_bFlags |= enPictureOk;
    return E_OK;
  }
  return E_ERROR;
}

int CSTIviewer::getLine( BYTE *Line, int Width )
{
  if( m_pHeader->Type==0x04 )
    return getLine16( (WORD*)Line, Width );
  return getLine8( Line, Width ); 
}

int CSTIviewer::getPalette( PALETTEENTRY *Pal )
{
  for(int i=0;i<256;i++)
  {
    Pal[i].peBlue=m_pPalette[i][0];
    Pal[i].peGreen=m_pPalette[i][1];
    Pal[i].peRed=m_pPalette[i][2];
  }
  return E_OK; 
}

int CSTIviewer::getLine16( unsigned short *Line, int Width )
{
  memmove(Line, m_pwPosition, Width<<1);
  m_pwPosition  +=  Width;
  return E_OK;
}

int CSTIviewer::getLine8( BYTE *Line, int Width )
{
  for(int i=0; i<Width; )
  {
    int z=(*m_pbPos & 0x7f );
    if( *m_pbPos & 0x80 )
    {
      i     +=  z;
      Line  +=z;    /*      for(; z; z--, *Line++=0); */
      m_pbPos++;
      if( *m_pbPos & 0x80 ) continue; 
      z = *m_pbPos;
    }
    m_pbPos++;
    memmove(Line, m_pbPos, z);
    m_pbPos += z;
    Line    += z;
    i       += z;
  }
  return E_OK;
}

void CSTIviewer::swapLines( BYTE *Line, int height, int width )
{
  BYTE* pbTmp = new BYTE[width];
  for ( int i = 0; i < height/2; i++ )
  {
    memcpy( pbTmp, Line + i*width, width );
    memcpy( Line + i*width, Line + (height - i - 1) * width, width );
    memcpy( Line + (height - i - 1) * width, pbTmp, width );
  }
  delete [] pbTmp;
}

PBYTE CSTIviewer::getPalettePtr() const
{
	return m_pbBMPPaletteData;
}

PBYTE CSTIviewer::getImagePtr() const
{
  return m_pbBMPImageData;
}

PBITMAPINFO CSTIviewer::getBMPINFOptr() const
{
	return (PBITMAPINFO)(m_pbWholeBMPData + sizeof(BITMAPFILEHEADER));
}

PBITMAPFILEHEADER CSTIviewer::getBMPFHptr() const
{
	return (PBITMAPFILEHEADER)m_pbWholeBMPData;
}

BYTE CSTIviewer::getPixelColor( int x, int y ) const
{
  return m_pbBMPImageData[y*m_nAlignedWidth + x];
}

bool CSTIviewer::canPaint()
{
  return (( m_bFlags & enPictureOk ) && ( m_bFlags & enPageOk ));
}

void CSTIviewer::closeImage()
{
  if ( m_pbFileData )
  {
    delete [] m_pbFileData; m_pbFileData = NULL;
    delete [] m_pbWholeBMPData; m_pbWholeBMPData = NULL;
  }
  m_bFlags = 0;
}